import { Component, OnInit, Input, OnDestroy } from '@angular/core';
import { forkJoin, Subscription } from "rxjs";
import { finalize } from "rxjs/operators";
import { ClrDatagridComparatorInterface, ClrLoadingState } from "@clr/angular";
import {
    ScanningResultService,
    UserPermissionService,
    USERSTATICPERMISSION,
    VulnerabilityItem
} from "../../../../lib/services";
import { ChannelService } from "../../../../lib/services/channel.service";
import { ErrorHandler } from "../../../../lib/utils/error-handler";
import { DEFAULT_SUPPORTED_MIME_TYPE, SEVERITY_LEVEL_MAP, VULNERABILITY_SEVERITY } from "../../../../lib/utils/utils";

@Component({
    selector: 'hbr-vulnerabilities-grid',
    templateUrl: './result-grid.component.html',
    styleUrls: ['./scanning.scss']
})
export class ResultGridComponent implements OnInit, OnDestroy {
    scanningResults: VulnerabilityItem[] = [];
    dataCache: VulnerabilityItem[] = [];
    loading: boolean = false;
    shouldShowLoading: boolean = true;
    @Input() tagId: string;
    @Input() repositoryId: string;
    @Input() projectId: number;
    hasScanImagePermission: boolean;
    hasEnabledScanner: boolean = false;
    scanBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
    severitySort: ClrDatagridComparatorInterface<VulnerabilityItem>;
    sub: Subscription;
    constructor(
        private scanningService: ScanningResultService,
        private channel: ChannelService,
        private userPermissionService: UserPermissionService,
        private errorHandler: ErrorHandler,
    ) {
        const that = this;
        this.severitySort = {
            compare(a: VulnerabilityItem, b: VulnerabilityItem): number {
                return that.getLevel(a) - that.getLevel(b);
            }
        };
    }

    ngOnInit(): void {
        this.loadResults(this.repositoryId, this.tagId);
        this.getScanPermissions(this.projectId);
        if (!this.sub) {
            this.channel.ArtifactDetail$.subscribe(tag => {
                this.loadResults(this.repositoryId, this.tagId);
            });
        }
    }
    ngOnDestroy() {
        if (this.sub) {
            this.sub.unsubscribe();
            this.sub = null;
        }
    }
    getLevel(v: VulnerabilityItem): number {
        if (v && v.severity && SEVERITY_LEVEL_MAP[v.severity]) {
           return SEVERITY_LEVEL_MAP[v.severity];
        }
        return 0;
    }
    getProjectScanner(): void {
        this.hasEnabledScanner = false;
        this.scanBtnState = ClrLoadingState.LOADING;
        this.scanningService.getProjectScanner(this.projectId)
            .subscribe(response => {
                if (response && "{}" !== JSON.stringify(response) && !response.disabled
                    && response.health === "healthy") {
                    this.scanBtnState = ClrLoadingState.SUCCESS;
                    this.hasEnabledScanner = true;
                } else {
                    this.scanBtnState = ClrLoadingState.ERROR;
                }
            }, error => {
                this.scanBtnState = ClrLoadingState.ERROR;
            });
    }

    loadResults(repositoryId: string, tagId: string): void {
        // only show loading for one time
        if (this.shouldShowLoading) {
            this.loading = true;
            this.shouldShowLoading = false;
        }
        this.scanningService.getVulnerabilityScanningResults(repositoryId, tagId)
            .pipe(finalize(() => this.loading = false))
            .subscribe((results) => {
                if (results && results[DEFAULT_SUPPORTED_MIME_TYPE]) {
                    let report = results[DEFAULT_SUPPORTED_MIME_TYPE];
                    if (report.vulnerabilities) {
                        this.dataCache = report.vulnerabilities;
                        this.scanningResults = this.dataCache.filter((item: VulnerabilityItem) => item.id !== '');
                        // sort
                        if (this.scanningResults) {
                            this.scanningResults.sort(((a, b) => this.getLevel(b) - this.getLevel(a)));
                        }
                        return;
                    }
                }
            });
    }

    // TODO: Should query from back-end service
    filterVulnerabilities(terms: string): void {
        if (terms.trim() === '') {
            this.scanningResults = this.dataCache.filter((item: VulnerabilityItem) => item.id !== '');
        } else {
            this.scanningResults = this.dataCache.filter((item: VulnerabilityItem) => this._regexpFilter(terms, item.package));
        }
    }

    refresh(): void {
        this.loadResults(this.repositoryId, this.tagId);
    }

    severityText(severity: string): string {
        switch (severity) {
            case VULNERABILITY_SEVERITY.CRITICAL:
                return 'VULNERABILITY.SEVERITY.CRITICAL';
            case VULNERABILITY_SEVERITY.HIGH:
                return 'VULNERABILITY.SEVERITY.HIGH';
            case VULNERABILITY_SEVERITY.MEDIUM:
                return 'VULNERABILITY.SEVERITY.MEDIUM';
            case VULNERABILITY_SEVERITY.LOW:
                return 'VULNERABILITY.SEVERITY.LOW';
            case VULNERABILITY_SEVERITY.NEGLIGIBLE:
                return 'VULNERABILITY.SEVERITY.NEGLIGIBLE';
            case VULNERABILITY_SEVERITY.UNKNOWN:
                return 'VULNERABILITY.SEVERITY.UNKNOWN';
            default:
                return 'UNKNOWN';
        }
    }

    _regexpFilter(terms: string, testedValue: any): boolean {
        let reg = new RegExp('.*' + terms + '.*', 'i');
        return reg.test(testedValue);
    }

    scanNow(): void {
        this.channel.publishScanEvent(this.repositoryId + "/" + this.tagId);
    }
    getScanPermissions(projectId: number): void {

        const hasScanImagePermission = this.userPermissionService.getPermission(projectId,
            USERSTATICPERMISSION.REPOSITORY_TAG_SCAN_JOB.KEY, USERSTATICPERMISSION.REPOSITORY_TAG_SCAN_JOB.VALUE.CREATE);
        forkJoin(hasScanImagePermission).subscribe(permissions => {
            this.hasScanImagePermission = permissions[0] as boolean;
            if (this.projectId && this.hasScanImagePermission) {
                this.getProjectScanner();
            }
        }, error => {
            this.errorHandler.error(error);
        });
    }
}
